/**
  @Author : chenyq
*/

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
	"math/rand"
	. "strings"
	"time"
)


const DOC_TYPE = "eduObj"
// 根据指定的查询字符串实现富查询
func getUserByQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {

	resultsIterator, err := stub.GetQueryResult(queryString)
	if err != nil {
		return nil, err
	}
	defer  resultsIterator.Close()

	// buffer is a JSON array containing QueryRecords
	var buffer bytes.Buffer

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString("::")
		}

		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		bArrayMemberAlreadyWritten = true
	}

	fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
	return buffer.Bytes(), nil

}


// 获取用户
//args: 0 账号，
func (t *MusicChaincode) getUser(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1{
		return shim.Error("006:Incorrect number of arguments . need 1")
	}
	state, e := stub.GetState(args[0])
	if e!=nil{
		return shim.Error("no this user")
	}
	if state==nil {
		return shim.Error("no this user")
	}
	return shim.Success(state)
}

// 获取用户
//args: 0 字段名， 1 值，
func (t *MusicChaincode) getUsers(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args)!=2{
		return shim.Error("006:Incorrect number of arguments . need 2")
	}
	queryString := fmt.Sprintf("{\"selector\":{\"%s\":\"%s\"}}", args[0], args[1])

	byQueryString, e := getUserByQueryString(stub, queryString)
	if e!=nil {
		return shim.Error("failed to get user message")
	}
	fmt.Println(byQueryString)
	return shim.Success(byQueryString)


}

// 审批接口
//args: 0 举报ID ,1 审批人ID, 审批结果: 0 通过 ,1 驳回，
func (t *MusicChaincode) approval(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args)!=3{
		return shim.Error("006:Incorrect number of arguments . need 3")
	}
	state, e := stub.GetState(args[1])
	if e!= nil || state==nil {
		return shim.Error("007: cannot get message for this person")
	}
	getState, e := stub.GetState(args[0] + "-" + args[1])
	fmt.Println("judge message :"+string(getState))
	if getState != nil{
		return shim.Error("014: this people has judged already")
	}

	per := Person{}
	e = json.Unmarshal(state, &per)
	if e != nil{
		return shim.Error("009:user message to json failed")
	}

	reByte, e := stub.GetState(args[0])
	if e!=nil || reByte==nil{
		return shim.Error("010 no this report message")
	}
	rep := Report{}
	e = json.Unmarshal(reByte, &rep)
	if e != nil {
		return shim.Error("011 : report to json failed:"+e.Error())
	}
	split := Split(rep.Judges, "::")
	fmt.Println(split)
	b :=false
	for _, i:= range split{
		fmt.Println(i)
		p := Person{}
		e := json.Unmarshal([]byte(i), &p)
		if e!=nil{
			return shim.Error("012: error to json judges:"+e.Error())
		}
		if args[1] == p.ID{
			fmt.Println("got equals people"+p.Name+"ID"+ p.ID)
			b=true
			break
		}
	}
	if b {
		if args[2]=="0"{
			rep.Count++
			marshal, _ := json.Marshal(rep)
			e = stub.PutState(args[0], marshal)
			fmt.Println("yes : " +string(marshal))
			if e!=nil {
				return shim.Error("016: store report message error")
			}
		}
		e := stub.PutState(args[0]+"-"+args[1], []byte("1"))
		if e!=nil{
			return shim.Error("013: judge put state failed")
		}
	}else {
		return shim.Success([]byte("Approval failed , The current user may not be a judge"))
	}
	return shim.Success([]byte("success"))

}

// 举报接口
//args:0：ID,1:用户ID，2:歌名，3:歌手，4:举报连接
// return :审批人账号列表
func (t *MusicChaincode) report(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 5{
		return shim.Error("006:Incorrect number of arguments . need 5")
	}
	state, e := stub.GetState(args[1])
	if state==nil {
		return shim.Error(" no this person")
	}

	report := Report{ID: args[0], PerID: args[1], Name: args[2], User: args[3], Url: args[4],Judges:"",Count:0}

	queryString := "{\"selector\":{\"Role\":\"0\"}}"

	byQueryString, e := getUserByQueryString(stub,queryString)
	s := string(byQueryString)
	split := Split(s, "::")
	if len(split)>5{
		ints := generateRandomNumber(0, len(split), 5)
		fmt.Println(ints)
		re := ""
		for _,nn := range ints{
			re+= (split[nn]+"::")
		}

		s=re[0:(len(re)-2)]

		byQueryString = []byte(s)
	}
	report.Judges = s
	bytes2, e := json.Marshal(report)
	if e!=nil{
		return shim.Success([]byte("json序列化失败"))
	}
	e = stub.PutState(args[0], bytes2)
	if e!=nil{
		return shim.Success([]byte("数据存储状态数据库异常"))
	}
	return shim.Success(byQueryString)
}

func generateRandomNumber(start int, end int, count int) []int {
	//范围检查
	if end < start || (end-start) < count {
		return nil
	}

	//存放结果的slice
	nums := make([]int, 0)
	//随机数生成器，加入时间戳保证每次生成的随机数不一样
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for len(nums) < count {
		//生成随机数
		num := r.Intn((end - start)) + start

		//查重
		exist := false
		for _, v := range nums {
			if v == num {
				exist = true
				break
			}
		}

		if !exist {
			nums = append(nums, num)
		}
	}

	return nums
}
// 为用户赋予审批权限
//args: 0 账号，1 role :角色,
func (t *MusicChaincode) update(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1{
		return shim.Error("006:Incorrect number of arguments . need 1")
	}
	state, e := stub.GetState(args[0])
	if e!=nil{
		return shim.Error("no this perple ,please register")
	}
	if state==nil {
		return shim.Error("no this perple ,please register")
	}

	var per Person;
	e = json.Unmarshal(state, &per)
	if e!=nil{
		return shim.Error("序列化用户信息失败")
	}
	per.Role = "0"
	perB, e := json.Marshal(per)
	if e!=nil{
		return shim.Error("序列化用户信息失败")
	}
	e = stub.PutState(args[0], perB);
	if e!=nil{
		return shim.Error("store user falied")
	}
	return shim.Success([]byte("success"))
}


// 注册接口
//args: 0 ID，1 Name :名字，2Role:角色，,3 Mess: 备注,
func (t *MusicChaincode) register(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 4{
		return shim.Error("006:Incorrect number of arguments . need 4")
	}
	state, e := stub.GetState(args[0])
	if e!=nil{
		return shim.Error("get user message failed")
	}
	if state!=nil {
		return shim.Error("001:user is already exist")
	}

	per := Person{ID:args[0],Name:args[1],Role:args[2],Mess:args[3]}
	perB, e := json.Marshal(per)
	if e!=nil{
		return shim.Error("序列化用户信息失败")

	}
	e = stub.PutState(args[0], perB);
	if e!=nil{
		return shim.Error("store user failed")
	}
	return shim.Success([]byte("success"))
}

//////////////////////
// 保存edu
// args: education
func PutEdu(stub shim.ChaincodeStubInterface, edu Education) ([]byte, bool) {

	edu.ObjectType = DOC_TYPE

	b, err := json.Marshal(edu)
	if err != nil {
		return nil, false
	}

	// 保存edu状态
	err = stub.PutState(edu.EntityID, b)
	if err != nil {
		return nil, false
	}

	return b, true
}

// 根据身份证号码查询信息状态
// args: entityID
func GetEduInfo(stub shim.ChaincodeStubInterface, entityID string) (Education, bool)  {
	var edu Education
	// 根据身份证号码查询信息状态
	b, err := stub.GetState(entityID)
	if err != nil {
		return edu, false
	}

	if b == nil {
		return edu, false
	}

	// 对查询到的状态进行反序列化
	err = json.Unmarshal(b, &edu)
	if err != nil {
		return edu, false
	}

	// 返回结果
	return edu, true
}

// 根据指定的查询字符串实现富查询
func getEduByQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {

	resultsIterator, err := stub.GetQueryResult(queryString)
	if err != nil {
		return nil, err
	}
	defer  resultsIterator.Close()

	// buffer is a JSON array containing QueryRecords
	var buffer bytes.Buffer

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}

		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		bArrayMemberAlreadyWritten = true
	}

	fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())

	return buffer.Bytes(), nil

}

// 添加信息
// args: educationObject
// 身份证号为 key, Education 为 value
func (t *MusicChaincode) addEdu(stub shim.ChaincodeStubInterface, args []string) peer.Response {

	if len(args) != 2{
		return shim.Error("给定的参数个数不符合要求")
	}

	var edu Education
	err := json.Unmarshal([]byte(args[0]), &edu)
	if err != nil {
		return shim.Error("反序列化信息时发生错误")
	}

	// 
	_, exist := GetEduInfo(stub, edu.EntityID)
	if exist {
		return shim.Error("要添加已存在")
	}

	_, bl := PutEdu(stub, edu)
	if !bl {
		return shim.Error("保存信息时发生错误")
	}

	err = stub.SetEvent(args[1], []byte{})
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success([]byte("信息添加成功"))
}

// 根据歌手名字和歌手名查询
// args: CertNo, name
func (t *MusicChaincode) queryEduByCertNoAndName(stub shim.ChaincodeStubInterface, args []string) peer.Response {

	if len(args) != 2 {
		return shim.Error("给定的参数个数不符合要求")
	}
	CertNo := args[0]
	name := args[1]

	// 拼装CouchDB所需要的查询字符串(是标准的一个JSON串)
	// queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"eduObj\", \"CertNo\":\"%s\"}}", CertNo)
	queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"%s\", \"CertNo\":\"%s\", \"Name\":\"%s\"}}", DOC_TYPE, CertNo, name)

	// 查询数据
	result, err := getEduByQueryString(stub, queryString)
	if err != nil {
		return shim.Error("根据证书编号及姓名查询信息时发生错误")
	}
	if result == nil {
		return shim.Error("根据指定的证书编号及姓名没有查询到相关的信息")
	}
	return shim.Success(result)
}

// 根据查询详情（溯源）
// args: entityID
func (t *MusicChaincode) queryEduInfoByEntityID(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("给定的参数个数不符合要求")
	}

	// 根据身份证号码查询edu状态
	b, err := stub.GetState(args[0])
	if err != nil {
		return shim.Error("根据身份证号码查询信息失败")
	}

	if b == nil {
		return shim.Error("根据身份证号码没有查询到相关的信息")
	}

	// 对查询到的状态进行反序列化
	var edu Education
	err = json.Unmarshal(b, &edu)
	if err != nil {
		return  shim.Error("反序列化edu信息失败")
	}

	// 获取历史变更数据
	iterator, err := stub.GetHistoryForKey(edu.EntityID)
	if err != nil {
		return shim.Error("根据指定的身份证号码查询对应的历史变更数据失败")
	}
	defer iterator.Close()

	// 迭代处理
	var historys []HistoryItem
	var hisEdu Education
	for iterator.HasNext() {
		hisData, err := iterator.Next()
		if err != nil {
			return shim.Error("获取edu的历史变更数据失败")
		}

		var historyItem HistoryItem
		historyItem.TxId = hisData.TxId
		json.Unmarshal(hisData.Value, &hisEdu)

		if hisData.Value == nil {
			var empty Education
			historyItem.Education = empty
		}else {
			historyItem.Education = hisEdu
		}

		historys = append(historys, historyItem)

	}

	edu.Historys = historys

	// 返回
	result, err := json.Marshal(edu)
	if err != nil {
		return shim.Error("序列化edu信息时发生错误")
	}
	return shim.Success(result)
}

// 根据更新信息
// args: educationObject
func (t *MusicChaincode) updateEdu(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2{
		return shim.Error("给定的参数个数不符合要求")
	}

	var info Education
	err := json.Unmarshal([]byte(args[0]), &info)
	if err != nil {
		return  shim.Error("反序列化edu信息失败")
	}

	// 根据身份证号码查询信息
	result, bl := GetEduInfo(stub, info.EntityID)
	if !bl{
		return shim.Error("根据身份证号码查询信息时发生错误")
	}

	result.Name = info.Name
	result.BirthDay = info.BirthDay
	result.Nation = info.Nation
	result.Gender = info.Gender
	result.Place = info.Place
	result.EntityID = info.EntityID
	result.Photo = info.Photo


	result.EnrollDate = info.EnrollDate
	result.GraduationDate = info.GraduationDate
	result.SchoolName = info.SchoolName
	result.Major = info.Major
	result.QuaType = info.QuaType
	result.Length = info.Length
	result.Mode = info.Mode
	result.Level = info.Level
	result.Graduation = info.Graduation
	result.CertNo = info.CertNo;

	_, bl = PutEdu(stub, result)
	if !bl {
		return shim.Error("保存信息信息时发生错误")
	}

	err = stub.SetEvent(args[1], []byte{})
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success([]byte("信息更新成功"))
}

// 
// args: entityID
func (t *MusicChaincode) delEdu(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2{
		return shim.Error("给定的参数个数不符合要求")
	}

	/*var edu Education
	result, bl := GetEduInfo(stub, info.EntityID)
	err := json.Unmarshal(result, &edu)
	if err != nil {
		return shim.Error("反序列化信息时发生错误")
	}*/

	err := stub.DelState(args[0])
	if err != nil {
		return shim.Error("删除信息时发生错误")
	}

	err = stub.SetEvent(args[1], []byte{})
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success([]byte("信息删除成功"))
}




